package com.shm.leetcode;

import java.util.ArrayList;
import java.util.List;

/**
 * 1447. 最简分数
 * 给你一个整数 n ，请你返回所有 0 到 1 之间（不包括 0 和 1）满足分母小于等于  n 的 最简 分数 。分数可以以 任意 顺序返回。
 *
 *
 *
 * 示例 1：
 *
 * 输入：n = 2
 * 输出：["1/2"]
 * 解释："1/2" 是唯一一个分母小于等于 2 的最简分数。
 * 示例 2：
 *
 * 输入：n = 3
 * 输出：["1/2","1/3","2/3"]
 * 示例 3：
 *
 * 输入：n = 4
 * 输出：["1/2","1/3","1/4","2/3","3/4"]
 * 解释："2/4" 不是最简分数，因为它可以化简为 "1/2" 。
 * 示例 4：
 *
 * 输入：n = 1
 * 输出：[]
 *
 *
 * 提示：
 *
 * 1 <= n <= 100
 * @author SHM
 */
public class SimplifiedFractions {

    /**
     *  欧几里得算法
     * @param a
     * @param b
     * @return
     */
    int gcd(int a,int b){
        return b==0?a:gcd(b,a%b);
    }

    /**
     * 更相减损法
     * @param a
     * @param b
     * @return
     */
    int gcd_1(int a,int b){
        while (true) {
            if (a > b) {
                a -= b;
            } else if (a < b) {
                b -= a;
            } else {
                return a;
            }
        }
    }

    /**
     * 数论
     * 数据范围为 100100 且数值大小在 (0, 1)(0,1) 之间，因此枚举「分子 + 分母」的 O(n^2)O(n
     * 2
     *  ) 做法是可接受的。
     *
     * 于是问题转化为：如何快速判断两个数组成的分数是否为最简（即判断两个数的最大公约数是否为 11）。
     *
     * 快速求得 aa 和 bb 的最大公约数的主要方式有两种 :「更相减损法」和「欧几里得算法」，其中「欧几里得算法」的递归实现最为好写，复杂度为 O(\log{(a + b)})O(log(a+b))，在绝大多数的情况下适用，只有在需要实现高精度时，才会考虑使用「更相减损法」。
     *
     * 时间复杂度：枚举分子分母的复杂度为 O(n^2)O(n
     * 2
     *  )；判断两数是否能凑成最简分数复杂度为 O(\log{n})O(logn)。整体复杂度为 O(n^2 * \log{n})O(n
     * 2
     *  ∗logn)
     * 空间复杂度：忽略递归带来的额外空间开销，复杂度为 O(1)O(1)
     *
     * 作者：AC_OIer
     * 链接：https://leetcode-cn.com/problems/simplified-fractions/solution/gong-shui-san-xie-jian-dan-shu-lun-yun-y-wma5/
     * @param n
     * @return
     */
    public List<String> simplifiedFractions(int n) {
        List<String> ans = new ArrayList<>();
        for (int i = 1; i < n; i++) {
            for (int j = i+1; j <= n; j++) {
                if (gcd(i,j)==1){
                    ans.add(i+"/"+j);
                }
            }
        }
        return ans;
    }
}
